package com.fanrui.code.chess.mahjong.round;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import com.fanrui.code.chess.mahjong.AbstractMahjongTable;
import com.fanrui.code.chess.mahjong.BaoHuRules;
import com.fanrui.code.chess.mahjong.Mahjong;
import com.fanrui.code.chess.mahjong.MahjongGamer;
import com.fanrui.code.chess.mahjong.MahjongIncome;
import com.fanrui.code.chess.mahjong.MahjongStandings.StandingsOperationType;
import com.fanrui.code.chess.mahjong.StandingsRound;
import com.fanrui.code.chess.mahjong.MahjongIncome.MahjongIncomeInfo;
import com.fanrui.code.mahjong.util.MahjongCardUtil;
import com.fanrui.code.util.logger.Stdout;
import com.fanrui.proto.cmd.NotifyCanOperationCard;
import com.fanrui.proto.cmd.NotifyPlayerOperation;
import com.fanrui.proto.cmd.Operation;
import com.fanrui.proto.cmd.EveryHuType.IconmeType;
import com.fanrui.proto.cmd.ResPlayerOperation.Result;
import com.google.gson.annotations.Expose;

/**
 * 麻将出牌回合
 * @author Administrator
 *
 */
public class MahjongSendRound extends MahjongRound {

	private final static List<Operation> CANRESPONSE = Arrays.asList(Operation.PENG,Operation.GUO, Operation.BOOM,
			Operation.GANG_M, Operation.CHI);
	
	/**
	 * 玩家出牌的响应
	 */
	@Expose
	private MahjongOperationResponse response;
	
//	private final Collection<Long> waitNum;
	
	private final Map<Long, List<Operation>> waitNum;
	
	public MahjongSendRound(long currentId, AbstractMahjongTable<?> table, MahjongOperationResponse response) {
		super(currentId, CANRESPONSE, table);
		this.response = response;
		waitNum = new HashMap<Long, List<Operation>>();
		for(Long canOperationId : table.getSurvivalPlayers(currentId)) {
			waitNum.put(canOperationId, new ArrayList<Operation>());
		}
		//打出的牌没有万能牌效果
		response.getCard().setWannengType(null);
		filterCantOperation();
		table.lastSendCard = response.getCard();
		//通知哪些人能够操作
		if(waitNum != null && !waitNum.isEmpty()) {
			for (Long id : waitNum.keySet()) {
				MahjongGamer gamer = table.getGamerByPlayerID(id);
				List<Operation> list = waitNum.get(id);
				if(gamer != null && list != null && !list.isEmpty()) {
					NotifyCanOperationCard.Builder notify = NotifyCanOperationCard.newBuilder();
					for(Operation operation : list) {
						notify.addOperation(operation);
					}
					notify.setOperationMahjong(response.getCard().toCardCmd());
					gamer.send(notify.build());
				}
			}
		}
	}
	
	@Override
	public synchronized Result addResponse(MahjongOperationResponse response) {
		Result result = canOperationType(response);
		if (result != Result.Success) {
			return result;
		}
		MahjongGamer gamer = table.getGamerByPlayerID(response.getOperationId());
		Mahjong operationCard = response.getCard();
		if (gamer == null || operationCard == null ) {
			Stdout.GET().info(getInfo() + " 没有找到玩家或者响应的牌无效 不能进行响应.响应内容:" + response);
			return Result.ParameterInvalid;
		}
		if (response.getType() == Operation.GUO) {
			gamer.setGuoShouHuAuto(response.getCard());
		}
		if (response.getType() == Operation.CHI) {
			// 如果吃了过后手牌里只剩下吃的牌，那么这种情况是不能吃的
			List<Mahjong> cards = MahjongCardUtil.getInstance().getEatNotPlay(response.getCards());
			if (cards != null && !cards.isEmpty()) {
				for (Mahjong card : cards) {
					if(card != null) {
						if (MahjongCardUtil.getInstance().isCanEat(card, response.getCards(), gamer.getHandCards())) {
							Stdout.GET().info(getInfo() + " 不能进行吃牌，吃了手牌不能出牌.响应内容:" + response);
							return Result.CantOperation;
						}
					}
				}
			}
		}

		if (response.getType() == Operation.PENG || response.getType() == Operation.GANG_M) {// 杠碰牌
																								// 查看玩家是否能碰
			int needNum = response.getType() == Operation.PENG ? 2 : 3;
			
			if (MahjongCardUtil.getInstance().getCardNumFromHand(operationCard, gamer.getHandCards()) < needNum) {
				Stdout.GET().info(getInfo() + " 需要的手牌数量不足.响应内容:" + response);
				return Result.NotHavaCard;
			}
		}
		if (response.getType() == Operation.BOOM) {
			if (!MahjongCardUtil.getInstance().checkHu(operationCard, gamer, table.getGameRules())) {
				Stdout.GET().info(getInfo() + " 手牌没有达到胡牌的规则.玩家手牌:[" + gamer.getHandCards() + "]响应内容:" + response);
				return Result.CantHu;
			}
			if (gamer.getGuoShouHu() != null) {
				Stdout.GET().info(getInfo() + "不满足过手胡的规则.上次过手的倍/颗数：" + gamer.getGuoShouHu() + "响应内容:" + response);
				return Result.GuoShouHu;
				// }
			}
		}
		// 判断是否能够加入到操作中
		addResponseToList(response);
		return result;
	}

	@Override
	public Object doResponse() {
		if (playerResponse.size() < getCanResponseList().size()) {
			return null;
		}
		if(isDoResponse){
			return null;
		}
		isDoResponse = true;
		boolean none = true;
		for (MahjongOperationResponse response : playerResponse) {
			if (response.getType() != Operation.GUO) {
				none = false;
			}
		}
		if (none) {
			// 都不要 下家摸牌
			table.setTouchPlayerId(table.getNextExistSeatPlayerId(currentId));
			table.startRound(null);
		} else {
			filterResponse();
			Long huCurrentId = null;
			for (MahjongOperationResponse response : playerResponse) {
				// 找到一个合适的响应并执行
				MahjongRound round = RoundUtil.getInstance().createRound(this, response, table);
				if (response.getType() == Operation.BOOM) {
					huCurrentId = round.getCurrentId();
					round.before();
				} else {
					table.setCurrentRound(round);
				}
			}
			if (huCurrentId != null) {
				table.setHaveHu(true);
				table.startRound(null);
			}
		}
		return null;
	}
	
	/**
	 * 过滤掉当前不能执行的操作
	 * 
	 * @return Collection<MahjongOperationResponse>
	 */
	public void filterResponse() {
		// 排序
		Collections.sort(playerResponse);
		// 胡>杠>碰>吃>过
		// 根据规则 筛选 可以执行的操作
		Iterator<MahjongOperationResponse> iterator = playerResponse.iterator();
		Operation priority = null;
		while (iterator.hasNext()) {
			MahjongOperationResponse response = iterator.next();
			if (priority == null) {
				priority = response.getType();
			} else if (response.getType().getNumber() <= priority.getNumber()) {
				iterator.remove();
			}
		}
	}
	/**
	 * 过滤掉不能操作的玩家 void
	 */
	public void filterCantOperation() {
		if (waitNum != null && !waitNum.isEmpty()) {
			Iterator<Long> iterator = waitNum.keySet().iterator();
			while (iterator.hasNext()) {
				MahjongGamer gamer = table.getGamerByPlayerID(iterator.next());
				if (gamer != null) {
					List<Operation> list = gamer.canOperationCardList(response.getCard());
					if(list != null && !list.isEmpty()) {
						waitNum.put(gamer.getPlayerID(), list);
					}else {
						iterator.remove();
					}
				}
			}
		}
	}

	@Override
	public Collection<Long> getCanResponseList() {
		return waitNum.keySet();
	}

	@Override
	public String getInfo() {
		return String.format("牌桌ID:%s --- 玩家:%s 出牌回合", table.getID(), currentId);
	}

	@Override
	public void before() {
		notifySendCard(response);
		if (waitNum.isEmpty()) {
			doResponse();
		}
	}

	/**
	 * 通知有人出牌了
	 * 
	 * @param response
	 *            void
	 */
	public void notifySendCard(MahjongOperationResponse response) {
		MahjongGamer gamer = table.getGamerByPlayerID(currentId);
		gamer.addSendNumber();
		gamer.removeCard(response.getCard());
		NotifyPlayerOperation.Builder notify = NotifyPlayerOperation.newBuilder();
		notify.setUserID(response.getOperationId());
		notify.setType(Operation.SEND);
		notify.addMj(response.getCard().toCardCmd());
		Stdout.GET().info(toString());
		table.sendCard(currentId, response.getCard());
		table.broadcast(notify.build());
		table.broadcast(response, waitTime,waitNum.keySet());
		table.addRound(this);
		//跟庄
		if(table.getGameRules() instanceof BaoHuRules) {
			if(table.getGameRules().getRoomConfig().getBaoHuPlayStyle().getTwoGamerCannotGenZhuang() && 2==table.getGameRules().getRoomConfig().getPlayerNumber()) {
				return;
			}
		}else {
			if(table.getGameRules().getRoomConfig().getGanZhouPlayStyle().getTwoGamerCannotGenZhuang() && 2==table.getGameRules().getRoomConfig().getPlayerNumber()) {
				return;
			}
		}
		if(table.getSendCardNum() == table.getGameRules().getRoomConfig().getPlayerNumber()) {
			boolean isGen = true;
			List<Mahjong> sends = table.getSendCards();
			if(sends != null && !sends.isEmpty()) {
				Mahjong mj = sends.get(0);
				for (Mahjong m : table.getSendCards()) {
					if(!m.cardEquals(mj)) {
						isGen = false;
						break;
					}
				}
			}
			if(isGen) {
				// 庄家支付三家各1分
				accounting();
			}
		}
	}

	@Override
	public StandingsRound toStandingsRound() {
		return new StandingsRound(StandingsOperationType.Send, currentId, null, response.getCard());
	}

	@Override
	public MahjongOperationResponse getScoreResponse() {
		return response;
	}

	@Override
	public boolean isAoutoNext() {
		return false;
	}
	
	@Override
	public String toString() {
		MahjongGamer gamer = table.getGamerByPlayerID(currentId);
		return String.format("玩家%s出牌 %s 出牌后手牌：%s", currentId, response.getCard(), gamer.getHandCards());
	}
	
	/**
	 * 算账 void
	 */
	public Collection<MahjongIncome> accounting() {
		MahjongGamer loser = table.getGamerByPlayerID(currentId);
		int baseScore = table.getGameRules().getBaseScore();// 底分
		if(table.getGameRules().getRoomConfig().getBanker()) {
			baseScore *= 2;
		}
		Collection<Long> players;
		List<MahjongIncomeInfo> incomeInfo = new ArrayList<MahjongIncomeInfo>();
		int total = 0;
		ArrayList<MahjongIncome> list = new ArrayList<MahjongIncome>();
		IconmeType iconmeType = IconmeType.MiaoZhuang;
		players = table.getSurvivalPlayers(currentId);
		for (long id : players) {
			MahjongGamer gamer = table.getGamerByPlayerID(id);
			baseScore = gamer.getRealMoney(baseScore);
			MahjongIncome mi = new MahjongIncome(id, baseScore, iconmeType, null,
					MahjongIncomeInfo.build(currentId, baseScore));
			list.add(mi);
			gamer.addIncome(mi);
			incomeInfo.add(MahjongIncomeInfo.build(id, -baseScore));
			total += baseScore;
		}
		total = loser.getRealMoney(total);
		MahjongIncome win = new MahjongIncome(currentId, -total, iconmeType, null, incomeInfo);
		loser.addIncome(win);
		list.add(win);
		return list;
	}

}
